package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Override
    public PageResult search(RequestParams params) {
        try {
            SearchRequest request = new SearchRequest("hotel");
/*          request.source().query(QueryBuilders.matchAllQuery());*/
            //条件太多，可以构建一个专门的方法
            FunctionScoreQueryBuilder functionScoreQueryBuilder = buildBasicQuery(params, request);
            request.source().query(functionScoreQueryBuilder);
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.preTags("<em style='color:red'>");
            highlightBuilder.postTags("</em>");
            highlightBuilder.field("name");
            highlightBuilder.requireFieldMatch(false);
            request.source().highlighter(highlightBuilder);
            String location = params.getLocation();
            if(!StringUtils.isEmpty(location)){
                request.source()
                        .sort(SortBuilders.geoDistanceSort("location",new GeoPoint(location))
                                .order(SortOrder.ASC)
                                .unit(DistanceUnit.KILOMETERS));
            }
            //实现分页
            Integer page = params.getPage();
            Integer size = params.getSize();
            request.source().from((page-1)*size);
            request.source().size(size);
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            PageResult pageResult = handleResponse(response);
            return pageResult;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }


    private FunctionScoreQueryBuilder buildBasicQuery(RequestParams params, SearchRequest request) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if (!StringUtils.isEmpty(params.getKey())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("all",params.getKey()));
        }else{
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }

        if (!StringUtils.isEmpty(params.getBrand())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand",params.getBrand()));
        }

        if (!StringUtils.isEmpty(params.getCity())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("city",params.getCity()));
        }

        if (!StringUtils.isEmpty(params.getStarName())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("starName",params.getStarName()));
        }

        if(params.getMaxPrice()!=null && params.getMinPrice()!=null){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
        }

        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
                boolQueryBuilder, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(10)
                        )
                }
        );

        return functionScoreQuery;
    }

    private PageResult handleResponse(SearchResponse response) {
        SearchHits hits = response.getHits();
        System.out.println("总共有数据"+hits.getTotalHits().value+"条");
        SearchHit[] searchHits = hits.getHits();
        List<HotelDoc> list = new ArrayList<>();
        for (SearchHit searchHit : searchHits) {
            String json = searchHit.getSourceAsString();
            HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
            Object[] sortValues = searchHit.getSortValues();
            if(sortValues!=null&&sortValues.length>0){
                hotelDoc.setDistance(sortValues[0]);
            }
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            if(highlightFields!=null) {
                HighlightField highlightField = highlightFields.get("name");
                if(highlightField!=null){
                    Text[] texts = highlightField.getFragments();
                    if(texts!=null&&texts.length>0){
                        hotelDoc.setName(texts[0].toString());
                    }
                }
            }
            list.add(hotelDoc);
        }
        return new PageResult(hits.getTotalHits().value,list);
    }

    @Override
    public Map filters(RequestParams params) {
        try {
            SearchRequest request = new SearchRequest("hotel");
            /*          request.source().query(QueryBuilders.matchAllQuery());*/
            //条件太多，可以构建一个专门的方法
            FunctionScoreQueryBuilder functionScoreQueryBuilder = buildBasicQuery(params, request);
            request.source().query(functionScoreQueryBuilder);
            request.source().size(0);
            request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(10));
            request.source().aggregation(AggregationBuilders.terms("cityAgg").field("city").size(10));
            request.source().aggregation(AggregationBuilders.terms("starNameAgg").field("starName").size(10));
            SearchResponse response = null;
            response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            //解析response
            Aggregations aggregations = response.getAggregations();
            Terms brandTerm = aggregations.get("brandAgg");
            ArrayList<String> brandList = new ArrayList<>();
            List<? extends Terms.Bucket> brandBuckets = brandTerm.getBuckets();
            for (Terms.Bucket termBucket : brandBuckets) {
                brandList.add(termBucket.getKeyAsString());
            }
            Terms cityTerm = aggregations.get("cityAgg");
            ArrayList<String> cityList = new ArrayList<>();
            List<? extends Terms.Bucket> cityBuckets = cityTerm.getBuckets();
            for (Terms.Bucket termBucket : cityBuckets) {
                cityList.add(termBucket.getKeyAsString());
            }
            Terms starNameTerm = aggregations.get("starNameAgg");
            ArrayList<String> starNameList = new ArrayList<>();
            List<? extends Terms.Bucket> starNameBuckets = starNameTerm.getBuckets();
            for (Terms.Bucket termBucket : starNameBuckets) {
                starNameList.add(termBucket.getKeyAsString());
            }
            Map map = new HashMap<>();
            map.put("brand",brandList);
            map.put("city",cityList);
            map.put("starName",starNameList);
            return map;
        }
         catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public void saveOrUpdateEs(String jsonString) {
        Hotel hotel = JSON.parseObject(jsonString, Hotel.class);
        HotelDoc hotelDoc = new HotelDoc(hotel);
        IndexRequest request = new IndexRequest("hotel");
        request.id(hotelDoc.getId().toString());
        request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
        try {
            restHighLevelClient.index(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void deleteEs(String id) {
        DeleteRequest request = new DeleteRequest("hotel");
        request.id(id);
        try {
            restHighLevelClient.delete(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
